package tools

import (
	"encoding/binary"
	"encoding/json"
	"errors"
	"fmt"
	"math"
	"reflect"
	"runtime"
	"strconv"
	"strings"
	"time"
)

// 各种转换工具

const (
	defaultFromTag = "json"
	defaultToTag   = "db"
)

// 结构体数据复制, 主要通过tag转换,
// tag必须一致, 返回转换了的字段(首字母大写)
// 可以指定tag, 不指定则默认为 json - db 指定的时候必须传两个

// 结构体值转换, 一定场景(被复制结构体内成员为指针, 复制到的结构体内成员为非指针的时候)替代Copy函数,
// 添加 s2s:"-" 则此字段会被忽略赋值, 主要用于过滤更新操作时的 主键ID
// 试例:
// type DemoTo struct {
//     Id   int64  `json:"id"`
//     Data string `json:"data"`
// }
//
// type DemoFrom struct {
//     Id   int64  `json:"id" s2s:"-"`
//     Data string `json:"data"`
// }
// 进行赋值操作时, 只会赋值 Data成员
//
// PS: 传入参数from 和 to必须是可寻址的, 也就是必须传入指针
type s2sValue struct {
	Type       string        `json:"type"`
	OriginType reflect.Type  `json:"origin_type"`
	Value      reflect.Value `json:"value"`
}

func Struct2Struct(from interface{}, to interface{}, tags ...string) (err error, convertFields []interface{}) {
	var (
		fromTag, toTag string
	)

	if len(tags) > 0 {
		if len(tags) < 2 {
			return errors.New("参数错误"), nil
		}
		fromTag = tags[0]
		toTag = tags[1]
	} else {
		fromTag = defaultFromTag
		toTag = defaultToTag
	}

	fromVt := reflect.TypeOf(from)
	fromVal := reflect.ValueOf(from)
	toVt := reflect.TypeOf(to)
	toVal := reflect.ValueOf(to)

	if kd := fromVt.Kind(); kd != reflect.Struct {
		if kd := fromVt.Elem().Kind(); kd != reflect.Struct {
			return errors.New("the param is not a struct, please make sure"), nil
		} else {
			fromVt = fromVt.Elem()
			fromVal = fromVal.Elem()
		}
	}

	if kd := toVt.Kind(); kd != reflect.Struct {
		if kd := toVt.Elem().Kind(); kd != reflect.Struct {
			return errors.New("the param is not a struct, please make sure"), nil
		} else {
			toVt = toVt.Elem()
			toVal = toVal.Elem()
		}
	}

	// 首先构造 tag map
	copyMap := make(map[string]s2sValue)
	toFieldNum := toVal.NumField()
	for i := 0; i < toFieldNum; i++ {
		tagName := toVt.Field(i).Tag.Get(toTag)
		if tagName == "" {
			continue
		}
		copyMap[tagName] = s2sValue{
			Type:       toVal.Field(i).Type().String(),
			OriginType: toVal.Field(i).Type(),
			Value:      toVal.Field(i),
		}
	}

	fieldNum := fromVal.NumField()
	for i := 0; i < fieldNum; i++ {
		tagNameStr := fromVt.Field(i).Tag.Get(fromTag)
		tagNames := strings.Split(tagNameStr, ",")
		tagName := ""
		if len(tagNames) > 0 {
			tagName = tagNames[0]
		}
		if tagName == "" {
			continue
		}
		ignoreTag := fromVt.Field(i).Tag.Get("s2s")
		if ignoreTag == "-" {
			continue
		}

		fromType := fromVal.Field(i).Type().String()

		switch fromType {
		case "*string",
			"*int64", "*int32",
			"*int16", "*int8", "*int",
			"*uint64", "*uint32",
			"*uint16", "*uint8", "*uint",
			"*float64", "*float32":
			if fromVal.Field(i).IsNil() {
				continue
			}
		}

		if fromType == copyMap[tagName].Type {
			copyMap[tagName].Value.Set(fromVal.Field(i))
			convertFields = append(convertFields, fromVt.Field(i).Name)
		} else if strings.Replace(fromType, "*", "", -1) == copyMap[tagName].Type {
			copyMap[tagName].Value.Set(fromVal.Field(i).Elem())
			convertFields = append(convertFields, fromVt.Field(i).Name)
		} else if strings.Replace(copyMap[tagName].Type, "*", "", -1) == fromType {
			if copyMap[tagName].Value.IsNil() {
				copyMap[tagName].Value.Set(reflect.New(copyMap[tagName].OriginType.Elem()))
			}
			copyMap[tagName].Value.Elem().Set(fromVal.Field(i))
			convertFields = append(convertFields, fromVt.Field(i).Name)
		}
	}

	return
}

type Struct2MapValue struct {
	Value reflect.Value
	Type  reflect.Type
}

// 字段tag 加上 s2m:"-" 则会被忽略
// 例:
// type structToMap struct {
//	   Data int64 `json:"data" s2m:"-"`
// }
// 从结构体转为 map (适用于 数据库更新数据)
func Struct2Map(v interface{}, maps map[string]interface{}, originValue ...Struct2MapValue) (data map[string]interface{}, err error) {
	var (
		vt  reflect.Type
		val reflect.Value
	)

	if maps != nil {
		data = maps
	} else {
		data = make(map[string]interface{})
	}

	if len(originValue) > 0 {
		vt = originValue[0].Type
		val = originValue[0].Value
	} else {
		vt = reflect.TypeOf(v)
		val = reflect.ValueOf(v)
	}

	if kd := vt.Kind(); kd != reflect.Struct {
		if kd := vt.Elem().Kind(); kd != reflect.Struct {
			return nil, errors.New("the param is not a struct, please make sure")
		} else {
			vt = vt.Elem()
			val = val.Elem()
			if vt.Kind() != reflect.Struct {
				return nil, errors.New("the param is not a struct, please make sure")
			}
		}
	}

	fieldNum := val.NumField()
	for i := 0; i < fieldNum; i++ {
		fieldIgnore := vt.Field(i).Tag.Get("s2m")
		if fieldIgnore == "-" {
			continue
		}

		fieldName := vt.Field(i).Tag.Get("json")
		if fieldName == "" {
			data, _ = Struct2Map(val.Field(i), data, Struct2MapValue{
				Value: val.Field(i),
				Type:  vt.Field(i).Type,
			})
			continue
		}

		switch val.Field(i).Type().String() {
		case "string":
			data[fieldName] = val.Field(i).String()

		case "*int64", "*int32", "*int16", "*int8", "*int":
			if val.Field(i).IsNil() {
				continue
			}
			data[fieldName] = val.Field(i).Elem().Int()

		case "*uint64", "*uint32", "*uint16", "*uint8", "*uint":
			if val.Field(i).IsNil() {
				continue
			}
			data[fieldName] = val.Field(i).Elem().Uint()

		case "uint64", "uint32", "uint16", "uint8", "uint":
			data[fieldName] = val.Field(i).Uint()

		case "float64", "float32":
			data[fieldName] = val.Field(i).Float()

		case "*float64", "*float32":
			if val.Field(i).IsNil() {
				continue
			}
			data[fieldName] = val.Field(i).Elem().Float()

		case "int64", "int32", "int16", "int8", "int":
			data[fieldName] = val.Field(i).Int()
		default:
			data[fieldName] = val.Field(i).Interface()
		}
	}

	return
}

// String 类型赚字符串
func String(any interface{}) string {
	switch v := any.(type) {
	case nil:
		return ""
	case string:
		return v
	case int:
		return strconv.Itoa(v)
	case int8:
		return strconv.Itoa(int(v))
	case int16:
		return strconv.Itoa(int(v))
	case int32:
		return strconv.Itoa(int(v))
	case int64:
		return strconv.FormatInt(v, 10)
	case uint:
		return strconv.FormatUint(uint64(v), 10)
	case uint8:
		return strconv.FormatUint(uint64(v), 10)
	case uint16:
		return strconv.FormatUint(uint64(v), 10)
	case uint64:
		return strconv.FormatUint(v, 10)
	case float32:
		return strconv.FormatFloat(float64(v), 'f', -1, 32)
	case float64:
		return strconv.FormatFloat(v, 'f', -1, 64)
	case bool:
		return strconv.FormatBool(v)
	case []byte:
		return string(v)
	case time.Time:
		return v.String()
	case *time.Time:
		if v == nil {
			return ""
		}
		return v.String()
	default:
		if v == nil {
			return ""
		}

		if i, ok := v.(interface{ String() string }); ok {
			return i.String()
		}

		if i, ok := v.(interface{ Error() string }); ok {
			return i.Error()
		}

		var (
			rv   = reflect.ValueOf(v)
			kind = rv.Kind()
		)

		switch kind {
		case reflect.Chan,
			reflect.Map,
			reflect.Slice,
			reflect.Func,
			reflect.Ptr,
			reflect.Interface,
			reflect.UnsafePointer:
			if rv.IsNil() {
				return ""
			}
		case reflect.String:
			return rv.String()
		}

		if kind == reflect.Ptr {
			return String(rv.Elem().Interface())
		}

		if b, e := json.Marshal(v); e != nil {
			return fmt.Sprint(v)
		} else {
			return string(b)
		}
	}
}

func Int64(any interface{}) int64 {
	if any == nil {
		return 0
	}
	switch value := any.(type) {
	case int:
		return int64(value)
	case int8:
		return int64(value)
	case int16:
		return int64(value)
	case int32:
		return int64(value)
	case int64:
		return value
	case uint:
		return int64(value)
	case uint8:
		return int64(value)
	case uint16:
		return int64(value)
	case uint32:
		return int64(value)
	case uint64:
		return int64(value)
	case float32:
		return int64(value)
	case float64:
		return int64(value)
	case bool:
		if value {
			return 1
		}
		return 0
	case []byte:
		return int64(binary.LittleEndian.Uint64(fillUpSize(value, 8)))
	default:
		if f, ok := value.(interface{ Int64() int64 }); ok {
			return f.Int64()
		}
		s := String(value)
		isMinus := false
		if len(s) > 0 {
			if s[0] == '-' {
				isMinus = true
				s = s[1:]
			} else if s[0] == '+' {
				s = s[1:]
			}
		}
		// Hexadecimal
		if len(s) > 2 && s[0] == '0' && (s[1] == 'x' || s[1] == 'X') {
			if v, e := strconv.ParseInt(s[2:], 16, 64); e == nil {
				if isMinus {
					return -v
				}
				return v
			}
		}
		// Octal
		if len(s) > 1 && s[0] == '0' {
			if v, e := strconv.ParseInt(s[1:], 8, 64); e == nil {
				if isMinus {
					return -v
				}
				return v
			}
		}
		// Decimal
		if v, e := strconv.ParseInt(s, 10, 64); e == nil {
			if isMinus {
				return -v
			}
			return v
		}
		// Float64
		return int64(Float64(value))
	}
}

func Int(any interface{}) int {
	if any == nil {
		return 0
	}
	if v, ok := any.(int); ok {
		return v
	}
	return int(Int64(any))
}

func Float32(any interface{}) float32 {
	if any == nil {
		return 0
	}
	switch value := any.(type) {
	case float32:
		return value
	case float64:
		return float32(value)
	case []byte:
		return math.Float32frombits(binary.LittleEndian.Uint32(fillUpSize(value, 4)))
	default:
		if f, ok := value.(interface{ Float32() float32 }); ok {
			return f.Float32()
		}
		v, _ := strconv.ParseFloat(String(any), 64)
		return float32(v)
	}
}

func Float64(any interface{}) float64 {
	if any == nil {
		return 0
	}
	switch value := any.(type) {
	case float32:
		return float64(value)
	case float64:
		return value
	case []byte:
		return math.Float64frombits(binary.LittleEndian.Uint64(fillUpSize(value, 8)))
	default:
		if f, ok := value.(interface{ Float64() float64 }); ok {
			return f.Float64()
		}
		v, _ := strconv.ParseFloat(String(any), 64)
		return v
	}
}

func fillUpSize(b []byte, l int) []byte {
	if len(b) >= l {
		return b[:l]
	}
	c := make([]byte, l)
	copy(c, b)
	return c
}

type JsonToMapValue struct {
	Value map[string]interface{}
}

/**
zh:
返回值说明
param 1: int, 返回值的类型，
		 1为 interface{}值类型，
		 2为 空(nil)
param 2: 如果param为 1，则此返回值是有效返回值
en:
The description of return values
param 1: int, the type of return，
		 1 is interface{}, type of value，
		 2 is null(nil)
param 2: if param is 1，the return value is a valid return value.
*/
func (j *JsonToMapValue) Get(key ...string) (int, interface{}, string, error) {
	var (
		length int
		err    interface{}
		tmp    map[string]interface{}
	)
	defer func() {
		if err = recover(); err != nil {
			switch err.(type) {
			case runtime.Error: // 运行时错误
				fmt.Println("runtime error:", err)
			default: // 非运行时错误
				fmt.Println("error:", err)
			}
		}
	}()

	length = len(key)

	if length < 1 {
		return 2, nil, "", errors.New("数据不存在, 请传入正确的key")
	}

	// 如果传的是单值，则返回单值
	if length == 1 {
		// 首先获取一下key， 如果是存在的，则返回
		if v := j.Value[key[0]]; v != nil {
			return 1, v, reflect.TypeOf(v).String(), nil
		}

		return 2, nil, "", errors.New("数据不存在")
	}

	tmp = j.Value

	// 现在处理 多层map的情况
	for k, v := range key {
		if k == length-1 {
			if tmp[v] == nil {
				continue
			}
			return 1, tmp[v], reflect.TypeOf(tmp[v]).String(), nil
		}
		tmp = tmp[key[k]].(map[string]interface{})
	}

	// 不存在，返回空
	return 2, nil, "", errors.New("数据不存在")
}

func JsonToMap(value []byte) (*JsonToMapValue, error) {
	var m map[string]interface{}
	err := json.Unmarshal(value, &m)
	if err != nil {
		return nil, err
	}
	return &JsonToMapValue{
		Value: m,
	}, nil
}
